package org.ku.hero.api.protocol;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import org.ku.hero.util.ByteUtil;

import java.nio.charset.StandardCharsets;

/**
 *  一个完整的数据包应由起始符、命令单元、识别码、数据加密方式、数据单元长度、数据单元和校验码组成
 *  将byte转为无符号数:data & 0xff
 */
public class Packet {

    public static final int PACKAGE_MIN_LEN = 20;

    /** 起始符,固定##,这里在解析中就已经判断好了 **/
    private byte[] sfd = Const.SFD;

    /**
     *  命令单元
     */
    private byte cmd;

    /**
     * 应答标志
     */
    private byte ack;
    /**
     *  唯一识别码,17字节
     *  当传输车辆数据时，应使用车辆 VIN，其字码应符合GB16735 的规定
     */
    private String vin;
    /**
     *  加密方式,1字节
     *  0x01：数据不加密；
     *  0x02：数据经过 RSA 算法加密；
     *  0x03：数据经过 AES128 位算法加密；
     *  0xFE：表示异常，
     *  0xFF：表示无效
     */
    private byte encry;

    /**
     * 数据单元长度是数据单元的总字节数，2字节
     * 有效值范围：0～65531
     */
    private int length;

    /**
     * 数据单元
     */
    private byte[] body = Const.EMPTY_BYTES;

    /**
     * 校验码，1字节
     *  采用 BCC（异或校验）法，校验范围从命令单元的第一个
     *  字节开始，同后一字节异或，直到校验码前一字节为止，
     *  校验码占用一个字节，当数据单元存在加密时，应先加密
     *  后校验，先校验后解密
     */
    private byte crc;

    public Packet(Command command){
        this.cmd = command.cmd;
    }

    public boolean valid2BCC() {
        byte[] cd = getPackData();
        byte ccd = cd[cd.length-1];//校验码
        return crc == ccd;
    }

    public byte[] getPackData() {
        ByteBuf dd = Unpooled.buffer()
                .writeBytes(sfd)//起始符
                .writeByte(cmd)//命令标识
                .writeByte(ack)//应答标志
                .writeBytes(vin.getBytes(StandardCharsets.UTF_8))//VIN
                .writeByte(encry)//数据加密方式
                .writeShort(length)//数据单元长度
                .writeBytes(body);//数据单元
        //计算校验码
        byte cc = ByteUtil.getBcc(dd.array(), 2, body.length);
        dd.writeByte(cc);
        return dd.array();
    }


    public void setAck(byte ack) {
        this.ack = ack;
    }

    public void setVin(String vin) {
        this.vin = vin;
    }

    public void setEncry(byte encry) {
        this.encry = encry;
    }

    public void setLength(int length) {
        this.length = length;
    }

    public void setBody(byte[] body) {
        this.body = body;
    }

    public void setCrc(byte crc) {
        this.crc = crc;
    }

    public byte getAck() {
        return this.ack;
    }

    public String setVin() {
        return this.vin;
    }

    public byte getEncry() {
        return this.encry;
    }

    public int getLength() {
        return this.length;
    }

    public byte[] getBody() {
        return this.body;
    }

    public byte getCrc() {
        return this.crc;
    }

    public byte getCmd() {
        return cmd;
    }

    public String getVin() {
        return vin;
    }
}
